home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / MacPerl 5.1.3 / Mac_Perl_513_src / MacPerl5 / MPConsole.cp < prev    next >
Encoding:
Text File  |  1996-11-24  |  14.2 KB  |  678 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    MacPerl            -    Standalone Perl
  3. File        :    MPConsole.cp    -    Console interface for GUSI
  4. Author    :    Matthias Neeracher
  5. Language    :    MPW C/C++
  6.  
  7. $Log: MPConsole.cp,v $
  8. Revision 1.2  1994/05/04  02:49:57  neeri
  9. Safer Interrupts.
  10.  
  11. Revision 1.1  1994/02/27  23:04:58  neeri
  12. Initial revision
  13.  
  14. Revision 0.2  1993/08/30  00:00:00  neeri
  15. ShowWindow -> DoShowWindow
  16.  
  17. Revision 0.1  1993/08/14  00:00:00  neeri
  18. Remember rectangles    
  19.  
  20. *********************************************************************/
  21.  
  22. #include <GUSIFile_P.h>
  23.  
  24. #include <Resources.h>
  25. #include <Windows.h>
  26. #include <Errors.h>
  27. #include <Folders.h>
  28. #include <PLStringFuncs.h>
  29. #include <LowMem.h>
  30. #include <OSEvents.h>
  31.  
  32. #include <ioctl.h>
  33. #include <sys/types.h>
  34. #include <Signal.h>
  35. #include <ctype.h>
  36.  
  37. #include "MPConsole.h"
  38.  
  39. extern "C" {
  40. #include "MPGlobals.h"
  41. #include "MPAppleEvents.h"
  42. #include "MPWindow.h"
  43. #include "MPFile.h"
  44. #include "MPMain.h"
  45. }
  46.  
  47. #undef open
  48.  
  49. Boolean    gWantConsoleInput = false;
  50.  
  51. class MPConsoleSocket;                             // That's what this file's all about
  52.  
  53. class MPConsoleSocket : public Socket    {        
  54.     friend class MPConsoleSocketDomain;    
  55.     friend void CloseConsole(Ptr cookie);
  56.     friend Boolean DoRawConsole(Ptr cookie, char theChar);
  57.     friend void HarvestConsole(DPtr doc, MPConsoleSocket * sock);
  58.     friend int MPConsoleSpin(spin_msg msg, long arg);
  59.     
  60.                     MPConsoleSocket(DPtr window);
  61.                     
  62.     virtual         ~MPConsoleSocket();
  63.     
  64.     DPtr                    window;
  65.     Handle                input;
  66.     Boolean                nonblocking;
  67.     Boolean                eof;
  68.     Boolean                raw;
  69.     Boolean                echo;
  70. public:
  71.     void             SetRaw(Boolean newRaw, Boolean newEcho);
  72.     virtual int    read(void * buffer, int buflen);
  73.     virtual int write(void * buffer, int buflen);
  74.     virtual int    fcntl(unsigned int cmd, int arg);
  75.     virtual void pre_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
  76.     virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
  77.     virtual void post_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
  78.     virtual int    ioctl(unsigned int request, void *argp);
  79.     virtual int    isatty();
  80. };    
  81.  
  82. class MPConsoleSocketDomain : public FileSocketDomain {
  83.     enum {
  84.         stdInput,
  85.         stdOutput,
  86.         stdError,
  87.         stdConsole,
  88.         userConsole
  89.     } consoleClass;
  90. public:
  91.     MPConsoleSocketDomain()    :    FileSocketDomain(AF_UNSPEC, true, false)    {    }
  92.     
  93.     virtual Boolean Yours(const GUSIFileRef & ref, Request request);
  94.     virtual Socket * open(const GUSIFileRef & ref, int oflag);
  95. };
  96.  
  97. MPConsoleSocketDomain    MPConsoleSockets;
  98.  
  99. #if !defined(powerc) && !defined(__powerc)
  100. #pragma segment MPConsole
  101. #endif
  102.  
  103. /************************ MPConsoleSocket members ************************/
  104.  
  105. void HarvestConsole(DPtr doc, MPConsoleSocket * sock)
  106. {                
  107.     HLock((*doc->theText)->hText);
  108.     
  109.     char * chr = *(*doc->theText)->hText + (*doc->theText)->teLength;
  110.     char * end = *(*doc->theText)->hText + doc->u.cons.fence;
  111.  
  112.     if (gGotEof == doc) {
  113.         PtrAndHand(end, sock->input, chr - end);
  114.         
  115.         doc->u.cons.fence = (*doc->theText)->teLength;
  116.         sock->eof            = true;
  117.     } else 
  118.         while (chr-- > end)
  119.             if (*chr == '\n') {
  120.                 if (!(sock->raw && sock->echo)) {
  121.                     PtrAndHand(end, sock->input, ++chr - end);
  122.                     doc->u.cons.fence = chr - *(*doc->theText)->hText;
  123.                 }
  124.                 
  125.                 break;
  126.             }
  127.     
  128.     HUnlock((*doc->theText)->hText);
  129. }
  130.  
  131. MPConsoleSocket::MPConsoleSocket(DPtr window)
  132.     : window(window)
  133. {
  134.     nonblocking                    =    false;
  135.     eof                            =    false;
  136.     raw                            =     false;
  137.     input                            =    NewHandle(0);
  138.     
  139.     if (window)
  140.         window->u.cons.cookie    =    Ptr(this);
  141. }
  142.  
  143. void CloseConsole(Ptr cookie)
  144. {
  145.     if (cookie)
  146.         ((MPConsoleSocket *) cookie)->window = nil;
  147. }
  148.  
  149. MPConsoleSocket::~MPConsoleSocket()
  150. {
  151.     DisposeHandle(input);
  152.     
  153.     if (window) {
  154.         window->u.cons.cookie    = nil;
  155.         
  156.         if (!((WindowPeek) window->theWindow)->visible)
  157.             CloseMyWindow(window->theWindow);
  158.     }
  159. }
  160.  
  161. int MPConsoleSocket::fcntl(unsigned int cmd, int arg)
  162. {
  163.     switch (cmd)    {
  164.     case F_GETFL:
  165.         if (nonblocking)
  166.             return FNDELAY;
  167.         else
  168.             return 0;
  169.     case F_SETFL:
  170.         if (arg & FNDELAY)
  171.             nonblocking = true;
  172.         else
  173.             nonblocking = false;
  174.             
  175.         return 0;
  176.     default:
  177.         return GUSI_error(EOPNOTSUPP);
  178.     }
  179. }
  180.  
  181. int MPConsoleSocket::ioctl(unsigned int request, void *argp)
  182. {
  183.     switch (request)    {
  184.     case FIONBIO:
  185.         nonblocking    =    (Boolean) *(long *) argp;
  186.         
  187.         return 0;
  188.     case FIONREAD:
  189.         *(unsigned long *) argp    = GetHandleSize(input);
  190.         
  191.         return 0;
  192.     case FIOINTERACTIVE:
  193.         return 0;
  194.     case WIOSELECT:
  195.         if (window)
  196.             SelectWindow(window->theWindow);
  197.             
  198.         return 0;
  199.     default:
  200.         return GUSI_error(EOPNOTSUPP);
  201.     }
  202. }
  203.  
  204. int MPConsoleSocket::read(void * buffer, int buflen)
  205. {
  206.     int    avail;
  207.     
  208.     fwalk(fflush);
  209.     
  210.     if (nonblocking || raw) {
  211.         gWantConsoleInput    = true;
  212.         MPConsoleSpin(SP_STREAM_READ, 0);
  213.         gWantConsoleInput    = false;
  214.     }
  215.     
  216.     avail = int(GetHandleSize(input));
  217.     
  218.     if (!avail)    {
  219.         if (eof) {
  220.             eof = false;
  221.             
  222.             return 0;
  223.         }
  224.         if (!window)
  225.             return 0;
  226.         else if (nonblocking || raw)
  227.             return GUSI_error(EWOULDBLOCK);
  228.         else {
  229.             if (!((WindowPeek) window->theWindow)->visible)
  230.                 DoShowWindow(window->theWindow);
  231.             if (!((WindowPeek) window->theWindow)->hilited)
  232.                 SelectWindow(window->theWindow);
  233.                 
  234.             window->u.cons.selected = true;
  235.             ShowWindowStatus();
  236.             
  237.             gWantConsoleInput    = true;
  238.             SPIN(!(avail = int(GetHandleSize(input))) && !eof && window, SP_STREAM_READ, 0);
  239.             gWantConsoleInput    = false;
  240.     
  241.             if (!avail && eof)
  242.                 eof = false;
  243.                 
  244.             window->u.cons.selected = false;
  245.             ShowWindowStatus();
  246.         }
  247.     }
  248.         
  249.     buflen = min(avail, buflen);
  250.     
  251.     HLock(input);
  252.     memcpy(buffer, *input, buflen);
  253.     if (avail -= buflen)
  254.         memcpy(*input, *input+buflen, avail);
  255.     HUnlock(input);
  256.     SetHandleSize(input, avail);
  257.     
  258.     return buflen;
  259. }
  260.  
  261. int MPConsoleSocket::write(void * buffer, int buflen)
  262. {
  263.     static Boolean prechecked = false;
  264.     short    oldStart;
  265.     short    oldEnd;
  266.     
  267.     if (!window)
  268.         return GUSI_error(ESHUTDOWN);
  269.  
  270.     if (!prechecked) {
  271.         char *buf;
  272.         int    trylen = buflen;
  273.     
  274.         for (buf = (char *) buffer; trylen--; ++buf)
  275.             switch (*buf) {
  276.             case 7:    /* Meep meep */
  277.                 if (buf > (char *)buffer) {
  278.                     prechecked = true;
  279.                     write(buffer, buf - (char *)buffer);
  280.                     prechecked = false;
  281.                 }
  282.                 SysBeep(1);
  283.                 if (trylen)
  284.                     write(buf+1, trylen);
  285.                 
  286.                 return buflen;
  287.             case 8:    /* Delete */
  288.                 switch (buf - (char *)buffer) {
  289.                 case 1:
  290.                     /* Nothing to do, cancel last character */
  291.                     break;
  292.                 default:
  293.                     prechecked = true;
  294.                     write(buffer, buf - (char *)buffer-1);
  295.                     prechecked = false;
  296.                     break;
  297.                 case 0:
  298.                     /* Real delete */
  299.                     if (AllSelected(window->theText)) {
  300.                         if (window->u.cons.fence < 32767)
  301.                             window->u.cons.fence = 0;
  302.                     } else if ((*window->theText)->selStart == (*window->theText)->selEnd)
  303.                         if ((*window->theText)->selStart <= window->u.cons.fence)
  304.                             --window->u.cons.fence;
  305.                     TEKey(8, window->theText);
  306.                     AdjustScrollbars(window, false);
  307.                     ShowSelect(window);
  308.                     break;
  309.                 }            
  310.                 if (trylen)
  311.                     write(buf+1, trylen);
  312.                 
  313.                 return buflen;                
  314.             }
  315.     }
  316.     
  317.     HarvestConsole(window, this);
  318.     
  319.     if (buflen > window->u.cons.memory) {
  320.         buffer = (void *) (Ptr(buffer) + buflen - window->u.cons.memory);
  321.         buflen = window->u.cons.memory;
  322.     }
  323.     
  324.     window->u.cons.memory -= buflen;
  325.     EnforceMemory(window, window->theText);
  326.     window->u.cons.memory += buflen;
  327.     
  328.     oldStart    =    (*window->theText)->selStart;
  329.     oldEnd    =    (*window->theText)->selEnd;
  330.     
  331.     if (oldStart >= window->u.cons.fence)
  332.         oldStart += buflen;
  333.     if (oldEnd >= window->u.cons.fence)
  334.         oldEnd += buflen;
  335.         
  336.     TESetSelect(window->u.cons.fence, window->u.cons.fence, window->theText);
  337.     TEInsert(buffer, buflen, window->theText);
  338.  
  339.     if (!((WindowPeek) window->theWindow)->visible) {
  340.         HideControl(window->vScrollBar);
  341.         HideControl(window->hScrollBar);
  342.         
  343.         DoShowWindow(window->theWindow);
  344.         if (!((WindowPeek) window->theWindow)->hilited)
  345.             SelectWindow(window->theWindow);
  346.     }
  347.  
  348.     ShowSelect(window);
  349.     DrawPageExtras(window);
  350.     
  351.     TESetSelect(oldStart, oldEnd, window->theText);
  352.  
  353.     if (window->u.cons.fence < 32767)
  354.         window->u.cons.fence += buflen;
  355.     
  356.     return buflen;
  357. }
  358.  
  359. static Boolean StatusNeedsUpdate = false;
  360.  
  361. void MPConsoleSocket::pre_select(Boolean canRead, Boolean, Boolean)
  362. {
  363.     if (canRead && window) {
  364.         gWantConsoleInput    = true;
  365.         StatusNeedsUpdate = window->u.cons.selected = true;
  366.         
  367.         if (!((WindowPeek) window->theWindow)->visible)
  368.             DoShowWindow(window->theWindow);
  369.     }
  370. }
  371.  
  372. void MPConsoleSocket::post_select(Boolean canRead, Boolean, Boolean)
  373. {
  374.     if (canRead && window) {
  375.         gWantConsoleInput    = false;
  376.         StatusNeedsUpdate = window->u.cons.selected = false;
  377.     }
  378. }
  379.  
  380. int MPConsoleSocket::select(Boolean * canRead, Boolean * canWrite, Boolean * exception)
  381. {
  382.     int        goodies     =     0;
  383.  
  384.     if (StatusNeedsUpdate) {
  385.         ShowWindowStatus();
  386.         
  387.         StatusNeedsUpdate = false;
  388.     }
  389.         
  390.     if (canRead)
  391.         if (*canRead = (GetHandleSize(input) > 0 || eof))
  392.             ++goodies;
  393.     
  394.     if (canWrite) {
  395.         *canWrite = true;
  396.         ++goodies;
  397.     }
  398.     
  399.     if (exception)
  400.         *exception = false;
  401.     
  402.     return goodies;
  403. }
  404.  
  405. int MPConsoleSocket::isatty()
  406. {
  407.     return 1;
  408. }
  409.  
  410. void MPConsoleSocket::SetRaw(Boolean newRaw, Boolean newEcho)
  411. {
  412.     if (raw && !newRaw) {
  413.         HLock(input);
  414.         char *    checkInput        =    *input;
  415.         char *    checkedInput    =    *input;
  416.         
  417.         for (int len = GetHandleSize(input); len--; ++checkInput)
  418.             switch (*checkInput) {
  419.             case 8:
  420.                 if (checkedInput > *input)
  421.                     --checkedInput;
  422.                 break;
  423.             case '\t':
  424.             case '\n':
  425.                 *checkedInput++ = *checkInput;
  426.                 break;
  427.             case 0x7F:
  428.                 break;
  429.             default:
  430.                 if (*checkInput >= 32)
  431.                     *checkedInput++ = *checkInput;
  432.                 break;
  433.             }
  434.         window->u.cons.fence = (*window->theText)->teLength;
  435.         write(*input, checkedInput - *input);
  436.         HUnlock(input);
  437.         SetHandleSize(input, 0);
  438.     } else if (!raw && newRaw)
  439.         HarvestConsole(window, this);
  440.         
  441.     raw = newRaw;
  442.     echo = newEcho;
  443. }
  444.  
  445. Boolean DoRawConsole(Ptr cookie, char theChar)
  446. {
  447.     if (cookie) {
  448.         MPConsoleSocket *    sock    = (MPConsoleSocket *) cookie;
  449.         if (sock->raw) {
  450.             PtrAndHand(&theChar, sock->input, 1);
  451.             
  452.             return !sock->echo;
  453.         }
  454.     }
  455.     return false;
  456. }
  457.  
  458. /********************* MPConsoleSocketDomain members **********************/
  459.  
  460. Boolean MPConsoleSocketDomain::Yours(const GUSIFileRef & ref, FileSocketDomain::Request request)
  461. {
  462.     if (ref.spec || (request != willOpen))
  463.         return false;
  464.     
  465.     switch (ref.name[4] | 0x20) {
  466.     case 's':
  467.         if (gRemoteControl)
  468.             return false;
  469.         if ((ref.name[5] | 0x20) != 't' || (ref.name[6] | 0x20) != 'd')
  470.             return false;
  471.         switch (ref.name[7] | 0x20) {
  472.         case 'i':
  473.             if ((ref.name[8] | 0x20) != 'n' || ref.name[9])
  474.                 return false;
  475.             consoleClass = stdInput;
  476.             
  477.             return true;
  478.         case 'o':
  479.             if ((ref.name[8] | 0x20) != 'u' || (ref.name[9] | 0x20) != 't' || ref.name[10])
  480.                 return false;
  481.             consoleClass = stdOutput;
  482.             
  483.             return true;
  484.         case 'e':
  485.             if ((ref.name[8] | 0x20) != 'r' || (ref.name[9] | 0x20) != 'r' || ref.name[10])
  486.                 return false;
  487.             consoleClass = stdError;
  488.             
  489.             return true;
  490.         default:
  491.             return false;
  492.         }
  493.     case 'c':
  494.         if (    (ref.name[5] | 0x20) != 'o' || (ref.name[6] | 0x20) != 'n'
  495.             || (ref.name[7] | 0x20) != 's' || (ref.name[8] | 0x20) != 'o'
  496.             || (ref.name[9] | 0x20) != 'l' || (ref.name[10] | 0x20) != 'e')
  497.             return false;
  498.         switch (ref.name[11]) {
  499.         case 0:
  500.             consoleClass = stdConsole;
  501.             
  502.             return true;
  503.         case ':':
  504.             consoleClass = userConsole;
  505.             
  506.             return true;
  507.         default:
  508.             return false;
  509.         }
  510.     default:
  511.         return false;
  512.     }
  513. }
  514.  
  515. Socket * MPConsoleSocketDomain::open(const GUSIFileRef & ref, int flags)
  516. {
  517.     DPtr                doc;
  518.     Socket *            sock = nil;
  519.     char                 title[256];
  520.     Boolean            nudoc = false;
  521.     
  522.     switch (consoleClass) {
  523.     case stdInput:
  524.         flags = O_RDONLY;
  525.         
  526.         break;
  527.     case stdOutput:
  528.         flags = O_WRONLY;
  529.         
  530.         break;
  531.     case stdError:
  532.         flags = O_WRONLY;
  533.         
  534.         break;
  535.     default:
  536.         break;
  537.     }
  538.  
  539.     if (consoleClass == userConsole) {
  540.         for (doc = gConsoleList; doc; doc = doc->u.cons.next)
  541.             if (doc->kind == kConsoleWindow) {
  542.                 getwtitle(doc->theWindow, title);
  543.                 
  544.                 if (equalstring(title, (char *) ref.name+12, false, true)) {
  545.                     if (doc->u.cons.cookie)
  546.                         sock = (Socket *) doc->u.cons.cookie;
  547.  
  548.                     goto found;
  549.                 }
  550.             }
  551.             
  552.         nudoc    = true;                
  553.         doc    = NewDocument(false, kConsoleWindow);
  554.         
  555.         setwtitle(doc->theWindow, (char *) ref.name+12);
  556.         
  557.         RestoreConsole(doc);
  558.     } else {
  559.         for (doc = gConsoleList; doc; doc = doc->u.cons.next)
  560.             if (doc->kind == kWorksheetWindow) {
  561.                 if (doc->u.cons.cookie)
  562.                     sock = (Socket *) doc->u.cons.cookie;
  563.  
  564.                 goto found;
  565.             }
  566.  
  567.         nudoc = true;
  568.         doc    = NewDocument(false, kWorksheetWindow);
  569.         SetWTitle(doc->theWindow, LMGetCurApName());
  570.  
  571.         RestoreConsole(doc);
  572.     }
  573.  
  574. found:    
  575.     if (!sock) {
  576.         errno = 0;
  577.         sock     = new MPConsoleSocket(doc);
  578.         
  579.         if (sock && errno) {
  580.             if (nudoc)
  581.                 CloseMyWindow(doc->theWindow);
  582.  
  583.             delete sock;
  584.             
  585.             return nil;
  586.         }
  587.     } else
  588.         ((MPConsoleSocket *)sock)->eof = false;
  589.  
  590.     if (!(flags & 1) && doc)
  591.         doc->u.cons.fence = (*doc->theText)->teLength;
  592.     else if (nudoc)
  593.         doc->u.cons.fence = 32767;
  594.  
  595.     return sock;
  596. }
  597.  
  598. /********************* A kinder, gentler, spin **********************/
  599.  
  600. extern "C" void Perl_my_exit(int status);
  601.  
  602. int MPConsoleSpin(spin_msg spin, long)
  603. {
  604.     if (!gInBackground && GUSIInterrupt() && gRunningPerl) {
  605.         FlushEvents(-1, 0);
  606.  
  607.         if (spin == SP_AUTO_SPIN || spin == SP_SLEEP)
  608.             Perl_my_exit(-128);
  609.         else
  610.             return -1;
  611.     }
  612.         
  613.     MainEvent(!gWantConsoleInput);
  614.     
  615.     for (DPtr doc = gConsoleList; doc; doc = doc->u.cons.next)
  616.         if (doc->dirty) {
  617.             if (doc->u.cons.cookie) 
  618.                 HarvestConsole(doc, (MPConsoleSocket *) doc->u.cons.cookie);
  619.             doc->dirty = false;
  620.         }
  621.     
  622.     return 0;
  623. }
  624.  
  625. /********************* Raw I/O **********************/
  626.  
  627. static int EmulateStty(FILE * tempFile, char * command)
  628. {
  629.     Boolean    setRaw    =    false;
  630.     Boolean    setEcho    =    false;
  631.     Boolean    flip;
  632.     
  633.     while (*command) {
  634.         flip = false;
  635.         if (isspace(*command)) {
  636.             do {
  637.                 ++command;
  638.             } while (*command && isspace(*command));
  639.             continue;
  640.         }
  641.         if (*command == '-') {
  642.             ++command;
  643.             flip = true;
  644.         }
  645.         if (!strncmp(command, "raw", 3) && (!command[3] || isspace(command[3]))) {
  646.             command += 3;
  647.             setRaw = !flip;
  648.         } else if (!strncmp(command, "sane", 4) && (!command[4] || isspace(command[4]))) {
  649.             command += 4;
  650.             setRaw = flip;
  651.         } else if (!strncmp(command, "echo", 4) && (!command[4] || isspace(command[4]))) {
  652.             command += 4;
  653.             setEcho = !flip;
  654.         } else
  655.             break;
  656.     }
  657.     
  658.     if (!*command)
  659.         command     =     "Dev:Console";
  660.  
  661.     GUSIFileRef    ref(command, FileSocketDomain::willOpen);
  662.     
  663.     if (ref.Domain() != &MPConsoleSockets)
  664.         return -1;
  665.     
  666.     MPConsoleSocket * console = (MPConsoleSocket *) ref.Domain()->open(ref, O_RDWR);
  667.     
  668.     console->SetRaw(setRaw, setEcho);
  669.     
  670.     return 0;
  671. }
  672.  
  673. void InitConsole()
  674. {
  675.     MPConsoleSockets.DontStrip();
  676.     AddWriteEmulationProc("stty", EmulateStty);
  677. }
  678.